#include "stdint.h"
#include "string.h"
#include "cpuid.h"
#include "text_video.h"
#include "oapl.h"

const uint32_t SSE_NEGONE_XMM_INT[4] = {-1,-1,-1,-1};

// internal init function, allocates memory space, zeros everything
static api_t api_init(size_t bits)
{
    api_t a;
    
    a = (api_t)malloc(sizeof(api_s));
    
    if(bits < 8){ bits = 8; }
    else if(bits % 8){ bits += 8 - (bits % 8); }
    
    a->bits = bits;
    a->bytes = a->bits / 8;
    a->i = (uint8_t *)malloc(a->bytes + SSE_ALIGN_SIZE);
    
    // make sure that we're aligned (just in case)
    while((size_t)a->i % SSE_ALIGN_SIZE){ a->i++; }
    
    memclr((uint8_t*)a->i, a->bytes);

    return a;
}

// init an API with another API (copy over the data and use the bit-size) {much faster due to not calling "api_get_current_size(x)"}
api_t api_init_c(api_t b)
{
    api_t a = api_init(b->bits);

    memcpy(a->i, b->i, b->bytes);
    return a;
}

// init an API with a 32-bit integer
api_t api_init_i(size_t bits, const uint32_t val)
{
    api_t a = api_init(bits);
    
    a->i[a->bytes-1] = (uint8_t)(val);
    a->i[a->bytes-2] = (uint8_t)(val >> 8);
    a->i[a->bytes-3] = (uint8_t)(val >> 16);
    a->i[a->bytes-4] = (uint8_t)(val >> 24);
    
    return a;
}

// convert an ASCII character to a hexadecimal byte
uint8_t ascii2hex(const char ascii)
{
    uint8_t hex;
    
    if(ascii >= '0' && ascii <= '9')
    { hex = ascii - '0'; }
    else if(ascii >= 'a' && ascii <= 'f')
    { hex = ascii - 'a' + 10; }
    else if(ascii >= 'A' && ascii <= 'F')
    { hex = ascii - 'A' + 10; }
    else
    { hex = 0; }
    
    return hex & 0x0F;
}

// "12345AB" -> 0x12345AB
void string2hexarray(const char * str, uint8_t * hex, size_t hex_size)
{
    size_t i, x;

    x = hex_size - 1;

    for(i = strlen(str); i > 0; i--)
    {
        hex[x] = ascii2hex(str[i-1]) & 0x0F;
        
        i--;
        if(!i){ break; }
        
        hex[x] |= (ascii2hex(str[i-1]) << 4) & 0xF0;

        if(!x){ break; }        
        x--;
    }    
}

// init an API with a numerical hex string (eg: "123ABC")
api_t api_init_s(size_t bits, const char * str)
{
    api_t a = api_init(bits);
    string2hexarray(str, a->i, a->bytes);
 
    return a;
}

// free all of the API_t's resources and zero the buffer (security?)
void api_destroy(api_t a)
{
    memclr(a->i, a->bytes);

    free((void *)a->i);
    free((void *)((size_t)a));
}

void api_print(api_t a)
{
    size_t i;

    for(i = 0; i < a->bytes - 1; i++)
    {
        if(a->i[i]){ break; }
    }

    for(; i < a->bytes; i++)
    { printf("%m", a->i[i]); }
}

// a <<= shift
void api_shl(api_t a, size_t shift)
{
    size_t i;
    
    for(i = 0; i < shift; i++)
    {
        asm("   api_shlb1:              \
                    clc;                \
                    pushf;              \
                .api_shlb2:             \
                    popf;               \
                    rclb $1, (%0);      \
                    pushf;              \
                    dec %0;             \
                    loopnz .api_shlb2;  \
                    popf;               "
        ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
          "c"(a->bytes)
          :"memory");
    }
}

// a >>= shift
void api_shr(api_t a, size_t shift)
{
    size_t i;
    
    for(i = 0; i < shift; i++)
    {
        asm("   api_shrb1:              \
                    clc;                \
                    pushf;              \
                .api_shrb2:             \
                    popf;               \
                    rcrb $1, (%0);      \
                    pushf;              \
                    inc %0;             \
                    loopnz .api_shrb2;  \
                    popf;               "
        ::"r"(a->i), 
          "c"(a->bytes)
         :"memory");
    }
}

// rotate a left
void api_rol(api_t a, size_t shift)
{
    size_t i;
    
    for(i = 0; i < shift; i++)
    {
        asm("   api_rolb1:              \
                    push %0;            \
                    clc;                \
                    pushf;              \
                .api_rolb2:             \
                    popf;               \
                    rclb $1, (%0);      \
                    pushf;              \
                    dec %0;             \
                    loopnz .api_rolb2;  \
                    popf;               \
                    pop %0;             \
                    jnc .api_rolb2_done;\
                    bts  $0, (%0);      \
                .api_rolb2_done:        "
        ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
          "c"(a->bytes)
          :"memory");
    }
}

// rotate a right
void api_ror(api_t a, size_t shift)
{
    size_t i;
    
    for(i = 0; i < shift; i++)
    {
        asm("   api_rorb1:              \
                    push %0;            \
                    clc;                \
                    pushf;              \
                .api_rorb2:             \
                    popf;               \
                    rcrb $1, (%0);      \
                    pushf;              \
                    inc %0;             \
                    loopnz .api_rorb2;  \
                    popf;               \
                    pop %0;             \
                    jnc .api_rorb2_done;\
                    bts  $7, (%0);      \
                .api_rorb2_done:        "
        ::"r"(a->i),
          "c"(a->bytes)
         :"memory");
    }
}

// A += "1234"
void api_add_s(api_t a, const char * str)
{
    uint8_t * hex = malloc(a->bytes);
    memclr(hex, a->bytes);
    string2hexarray(str, hex, a->bytes);

    asm("   api_addb_s1:                \
                clc;                    \
                pushf;                  \
            .api_addb_s2:               \
                movb (%1), %2;          \
                popf;                   \
                adcb %2, (%0);          \
                pushf;                  \
                dec %0;                 \
                dec %1;                 \
                loopnz .api_addb_s2;    \
                popf;                   "      
    ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
      "r"(hex + a->bytes - sizeof(uint8_t)), 
      "r"(0),
      "c"(a->bytes)
     :"memory"); 
}

// A += B
void api_add_self(api_t a, api_t b)
{
    asm("   api_addb_self1:             \
                clc;                    \
                pushf;                  \
            .api_addb_self2:            \
                movb (%1), %2;          \
                popf;                   \
                adcb %2, (%0);          \
                pushf;                  \
                dec %0;                 \
                dec %1;                 \
                loopnz .api_addb_self2; \
                popf;                   "      
    ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
      "r"(b->i + b->bytes - sizeof(uint8_t)), 
      "r"(0),
      "c"(a->bytes >= b->bytes ? a->bytes : b->bytes)
     :"memory"); 
}

// A + B = C
void api_add(api_t a, api_t b, api_t c)
{
    asm("   api_addb1:                  \
                clc;                    \
                pushf;                  \
            .api_addb2:                 \
                movb (%0), %3;          \
                popf;                   \
                adcb (%1), %3;          \
                movb %3, (%2);          \
                pushf;                  \
                dec %0;                 \
                dec %1;                 \
                dec %2;                 \
                loopnz .api_addb2;      \
                popf;                   "      
    ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
      "r"(b->i + b->bytes - sizeof(uint8_t)), 
      "r"(c->i + c->bytes - sizeof(uint8_t)),
      "r"(0),
      "c"(a->bytes >= b->bytes ? a->bytes : b->bytes)
     :"memory"); 
}

// A = ~A;
void api_not(api_t a)
{
    if(cpuid.features.SSE4_1 && !(a->bytes % SSE_ALIGN_SIZE))
    {
        asm("   .api_pnot_sse4:             \
                    movntdqa (%0), %%xmm0;  \
                    pxor (%1), %%xmm0;      \
                    movntdq %%xmm0, (%0);   \
                    sub $16, %0;            \
                    loopnz .api_pnot_sse4;  "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(SSE_NEGONE_XMM_INT), 
          "c"(a->bytes / SSE_ALIGN_SIZE)
         :"memory");                  
    }
    else if(cpuid.features.SSE2 && !(a->bytes % SSE_ALIGN_SIZE))
    {
        asm("   .api_pnot_sse2:             \
                    movdqa (%0), %%xmm0;    \
                    pxor (%1), %%xmm0;      \
                    movdqa %%xmm0, (%0);    \
                    sub $16, %0;            \
                    loopnz .api_pnot_sse2;  "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(SSE_NEGONE_XMM_INT), 
          "c"(a->bytes / SSE_ALIGN_SIZE)
         :"memory");                  
    }
    else if(!(a->bytes % 4))
    {
        asm("   .api_notl1:             \
                    notl (%0);          \
                    add $4, %0;         \
                    loopnz .api_notl1;  "
        ::"r"(a->i), 
          "c"(a->bytes / 4)
         :"memory");
    }
    else
    {
        asm("   .api_notb1:             \
                    notb (%0);          \
                    inc %0;             \
                    loopnz .api_notb1;  "
        ::"r"(a->i), 
          "c"(a->bytes)
         :"memory");
    }
}

// A++
void api_inc(api_t a)
{
    asm("   api_incb1:                  \
                clc;                    \
                addb $1, (%0);          \
                pushf;                  \
                dec %0;                 \
                dec %2;                 \
            .api_incb2:                 \
                popf;                   \
                adcb $0, (%0);          \
                jnc .api_incb2_done;    \
                pushf;                  \
                dec %0;                 \
                loopnz .api_incb2;      \
                popf;                   \
            .api_incb2_done:            "      
    ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
      "r"(1),
      "c"(a->bytes)
     :"memory"); 
}

// A |= "1234"
void api_or_s(api_t a, const char * str)
{
    uint8_t * hex = malloc(a->bytes + SSE_ALIGN_SIZE);
    while((size_t)hex % SSE_ALIGN_SIZE){ hex++; }
    memclr(hex, a->bytes);
    string2hexarray(str, hex, a->bytes);

    if(cpuid.features.SSE4_1 && !(a->bytes % SSE_ALIGN_SIZE))
    {
        asm("   .api_por_s_sse4:            \
                    movntdqa (%0), %%xmm0;  \
                    por (%1), %%xmm0;       \
                    movntdq %%xmm0, (%0);   \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    loopnz .api_por_s_sse4; "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(hex + a->bytes - SSE_ALIGN_SIZE), 
          "c"(a->bytes / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else if(cpuid.features.SSE2 && !(a->bytes % SSE_ALIGN_SIZE))
    {
        asm("   .api_por_s_sse2:            \
                    movdqa (%0), %%xmm0;    \
                    por (%1), %%xmm0;       \
                    movdqa %%xmm0, (%0);    \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    loopnz .api_por_s_sse2; "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(hex + a->bytes - SSE_ALIGN_SIZE), 
          "c"(a->bytes / SSE_ALIGN_SIZE)
         :"memory");    
    }
    if(!(a->bytes % sizeof(uint32_t)))
    {
        asm("   .api_orl_s2:                \
                    movl (%1), %2;          \
                    orl %2, (%0);           \
                    sub $4, %0;             \
                    sub $4, %1;             \
                    loopnz .api_orl_s2;     "      
        ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
          "r"(hex + a->bytes - sizeof(uint32_t)), 
          "r"(0),
          "c"(a->bytes / sizeof(uint32_t))
         :"memory"); 
    }
    else
    {
        asm("   .api_orb_s2:                \
                    movb (%1), %2;          \
                    orb %2, (%0);           \
                    dec %0;                 \
                    dec %1;                 \
                    loopnz .api_orb_s2;     "      
        ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
          "r"(hex + a->bytes - sizeof(uint8_t)), 
          "r"(0),
          "c"(a->bytes)
         :"memory"); 
    }
}

// A |= 0x12...
void api_or_set_i(api_t a, const uint8_t val)
{   
    asm("   .api_orb_set_i2:            \
                orb %1, (%0);           \
                dec %0;                 \
                loopnz .api_orb_set_i2; "  
    ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
      "r"(val),
      "c"(a->bytes)
     :"memory"); 
}

// A |= 0x12345678
void api_or_i(api_t a, const uint32_t val)
{
    uint8_t x[4];
    x[3] = (uint8_t)(val);
    x[2] = (uint8_t)(val >> 8);
    x[1] = (uint8_t)(val >> 16);
    x[0] = (uint8_t)(val >> 24);
    
    asm("   orl %1, (%0);           "      
    ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
      "r"(*(uint32_t*)(x))
     :"memory"); 
}

// A |= B
void api_or_self(api_t a, api_t b)
{
    asm("   .api_orb_self2:             \
                movb (%1), %2;          \
                orb %2, (%0);           \
                dec %0;                 \
                dec %1;                 \
                loopnz .api_orb_self2;  "      
    ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
      "r"(b->i + b->bytes - sizeof(uint8_t)), 
      "r"(0),
      "c"(a->bytes >= b->bytes ? b->bytes : a->bytes)
     :"memory"); 
}

// A | B = C
void api_or(api_t a, api_t b, api_t c)
{
    if(cpuid.features.SSE4_1 && !((a->bytes >= b->bytes ? b->bytes : a->bytes) % SSE_ALIGN_SIZE))
    {
        asm("   .api_por_sse4:              \
                    movntdqa (%0), %%xmm0;  \
                    por (%1), %%xmm0;       \
                    movntdq %%xmm0, (%2);   \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    sub $16, %2;            \
                    loopnz .api_por_sse4;   "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(b->i + b->bytes - SSE_ALIGN_SIZE), 
          "r"(c->i + c->bytes - SSE_ALIGN_SIZE),
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else if(cpuid.features.SSE2 && !((a->bytes >= b->bytes ? b->bytes : a->bytes) % SSE_ALIGN_SIZE))
    {
        asm("   .api_por_sse2:              \
                    movdqa (%0), %%xmm0;    \
                    por (%1), %%xmm0;       \
                    movdqa %%xmm0, (%2);    \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    sub $16, %2;            \
                    loopnz .api_por_sse2;   "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(b->i + b->bytes - SSE_ALIGN_SIZE), 
          "r"(c->i + c->bytes - SSE_ALIGN_SIZE),
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else if(!((a->bytes >= b->bytes ? b->bytes : a->bytes) % sizeof(uint32_t)))
    {
        asm("   .api_orl2:                  \
                    movl (%1), %3;          \
                    orl (%0), %3;           \
                    movl %3, (%2);          \
                    sub $4, %0;             \
                    sub $4, %1;             \
                    sub $4, %2;             \
                    loopnz .api_orl2;       "      
        ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
          "r"(b->i + b->bytes - sizeof(uint32_t)), 
          "r"(c->i + c->bytes - sizeof(uint32_t)),
          "r"(0),
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / sizeof(uint32_t))
         :"memory"); 
     }
    else
    {
        asm("   .api_orb2:                  \
                    movb (%1), %3;          \
                    orb (%0), %3;           \
                    movb %3, (%2);          \
                    dec %0;                 \
                    dec %1;                 \
                    dec %2;                 \
                    loopnz .api_orb2;       "      
        ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
          "r"(b->i + b->bytes - sizeof(uint8_t)), 
          "r"(c->i + c->bytes - sizeof(uint8_t)),
          "r"(0),
          "c"(a->bytes >= b->bytes ? b->bytes : a->bytes)
         :"memory"); 
     }
}

// A &= "1234"
void api_and_s(api_t a, const char * str)
{
    uint8_t * hex = malloc(a->bytes + SSE_ALIGN_SIZE);
    while((size_t)hex % SSE_ALIGN_SIZE){ hex++; }
    memclr(hex, a->bytes);
    string2hexarray(str, hex, a->bytes);

    if(cpuid.features.SSE4_1 && !(a->bytes % SSE_ALIGN_SIZE))
    {
        asm("   .api_pand_s_sse4:           \
                    movntdqa (%0), %%xmm0;  \
                    pand (%1), %%xmm0;      \
                    movntdq %%xmm0, (%0);   \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    loopnz .api_pand_s_sse4;"
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(hex + a->bytes - SSE_ALIGN_SIZE), 
          "c"(a->bytes / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else if(cpuid.features.SSE2 && !(a->bytes % SSE_ALIGN_SIZE))
    {
        asm("   .api_pand_s_sse2:           \
                    movdqa (%0), %%xmm0;    \
                    pand (%1), %%xmm0;      \
                    movdqa %%xmm0, (%0);    \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    loopnz .api_pand_s_sse2;"
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(hex + a->bytes - SSE_ALIGN_SIZE), 
          "c"(a->bytes / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else if(!(a->bytes % sizeof(uint32_t)))
    {
        asm("   .api_andl_s2:               \
                    movl (%1), %2;          \
                    andl %2, (%0);          \
                    sub $4, %0;             \
                    sub $4, %1;             \
                    loopnz .api_andl_s2;    "      
        ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
          "r"(hex + a->bytes - sizeof(uint32_t)), 
          "r"(0),
          "c"(a->bytes / sizeof(uint32_t))
         :"memory"); 
    }
    else
    {
        asm("   .api_andb_s2:               \
                    movb (%1), %2;          \
                    andb %2, (%0);          \
                    dec %0;                 \
                    dec %1;                 \
                    loopnz .api_andb_s2;    "      
        ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
          "r"(hex + a->bytes - sizeof(uint8_t)), 
          "r"(0),
          "c"(a->bytes)
         :"memory"); 
    }
}

// A &= 0x12...
void api_and_set_i(api_t a, const uint8_t val)
{   
    asm("   .api_andb_set_i2:           \
                andb %1, (%0);          \
                dec %0;                 \
                loopnz .api_andb_set_i2;"  
    ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
      "r"(val),
      "c"(a->bytes)
     :"memory"); 
}

// A &= 0x12345678
void api_and_i(api_t a, const uint32_t val)
{
    uint8_t x[4];
    x[3] = (uint8_t)(val);
    x[2] = (uint8_t)(val >> 8);
    x[1] = (uint8_t)(val >> 16);
    x[0] = (uint8_t)(val >> 24);
    
    asm("   andl %1, (%0);          "      
    ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
      "a"(*(uint32_t*)(x))
     :"memory"); 
}

// A &= B
void api_and_self(api_t a, api_t b)
{
    if(cpuid.features.SSE4_1 && !((a->bytes >= b->bytes ? b->bytes : a->bytes) % SSE_ALIGN_SIZE))
    {
        asm("   .api_pand_self_sse4:            \
                    movntdqa (%0), %%xmm0;      \
                    pand (%1), %%xmm0;          \
                    movntdq %%xmm0, (%0);       \
                    sub $16, %0;                \
                    sub $16, %1;                \
                    loopnz .api_pand_self_sse4; "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(b->i + b->bytes - SSE_ALIGN_SIZE), 
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else if(cpuid.features.SSE2 && !((a->bytes >= b->bytes ? b->bytes : a->bytes) % SSE_ALIGN_SIZE))
    {
        asm("   .api_pand_self_sse2:            \
                    movdqa (%0), %%xmm0;        \
                    pand (%1), %%xmm0;          \
                    movdqa %%xmm0, (%0);        \
                    sub $16, %0;                \
                    sub $16, %1;                \
                    loopnz .api_pand_self_sse2; "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(b->i + b->bytes - SSE_ALIGN_SIZE), 
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / SSE_ALIGN_SIZE)
         :"memory");    
    }
    
    else
    {
        asm("   .api_andb_self2:            \
                    movb (%1), %2;          \
                    andb %2, (%0);          \
                    dec %0;                 \
                    dec %1;                 \
                    loopnz .api_andb_self2; "      
        ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
          "r"(b->i + b->bytes - sizeof(uint8_t)), 
          "a"(0),
          "c"(a->bytes >= b->bytes ? b->bytes : a->bytes)
         :"memory"); 
    }
}

// A & B = C
void api_and(api_t a, api_t b, api_t c)
{
    if(cpuid.features.SSE4_1 && !((a->bytes >= b->bytes ? b->bytes : a->bytes) % SSE_ALIGN_SIZE))
    {
        asm("   .api_pand_sse4:             \
                    movntdqa (%0), %%xmm0;  \
                    pand (%1), %%xmm0;      \
                    movntdq %%xmm0, (%2);   \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    sub $16, %2;            \
                    loopnz .api_pand_sse4;  "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(b->i + b->bytes - SSE_ALIGN_SIZE), 
          "r"(c->i + c->bytes - SSE_ALIGN_SIZE),
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else if(cpuid.features.SSE2 && !((a->bytes >= b->bytes ? b->bytes : a->bytes) % SSE_ALIGN_SIZE))
    {
        asm("   .api_pand_sse2:             \
                    movdqa (%0), %%xmm0;    \
                    pand (%1), %%xmm0;      \
                    movdqa %%xmm0, (%2);    \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    sub $16, %2;            \
                    loopnz .api_pand_sse2;  "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(b->i + b->bytes - SSE_ALIGN_SIZE), 
          "r"(c->i + c->bytes - SSE_ALIGN_SIZE),
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else
    {
        asm("   .api_andb2:                 \
                    movb (%1), %3;          \
                    andb (%0), %3;          \
                    movb %3, (%2);          \
                    dec %0;                 \
                    dec %1;                 \
                    dec %2;                 \
                    loopnz .api_andb2;      "      
        ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
          "r"(b->i + b->bytes - sizeof(uint8_t)), 
          "r"(c->i + c->bytes - sizeof(uint8_t)),
          "a"(0),
          "c"(a->bytes >= b->bytes ? b->bytes : a->bytes)
         :"memory"); 
    }
}

// A ^= "1234"
void api_xor_s(api_t a, const char * str)
{
    uint8_t * hex = malloc(a->bytes + SSE_ALIGN_SIZE);
    while((size_t)hex % SSE_ALIGN_SIZE){ hex++; }
    memclr(hex, a->bytes);
    string2hexarray(str, hex, a->bytes);

    if(cpuid.features.SSE4_1 && !(a->bytes % SSE_ALIGN_SIZE))
    {
        asm("   .api_pxor_s_sse4:           \
                    movntdqa (%0), %%xmm0;  \
                    pxor (%1), %%xmm0;      \
                    movntdq %%xmm0, (%0);   \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    sub $16, %2;            \
                    loopnz .api_pxor_s_sse4;"
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(hex + a->bytes - SSE_ALIGN_SIZE), 
          "c"(a->bytes / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else if(cpuid.features.SSE2 && !(a->bytes % SSE_ALIGN_SIZE))
    {
        asm("   .api_pxor_s_sse2:           \
                    movdqa (%0), %%xmm0;    \
                    pxor (%1), %%xmm0;      \
                    movdqa %%xmm0, (%0);    \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    sub $16, %2;            \
                    loopnz .api_pxor_s_sse2;"
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(hex + a->bytes - SSE_ALIGN_SIZE), 
          "c"(a->bytes / SSE_ALIGN_SIZE)
         :"memory");    
    }
    else
    {
        asm("   .api_xorb_s2:               \
                    movb (%1), %2;          \
                    xorb %2, (%0);          \
                    dec %0;                 \
                    dec %1;                 \
                    loopnz .api_xorb_s2;    "      
        ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
          "r"(hex + a->bytes - sizeof(uint8_t)), 
          "a"(0),
          "c"(a->bytes)
         :"memory"); 
     }
}

// A ^= 0x12...
void api_xor_set_i(api_t a, const uint8_t val)
{
    asm("   .api_xorb_set_i2:           \
                xorb %1, (%0);          \
                dec %0;                 \
                loopnz .api_xorb_set_i2;"  
    ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
      "a"(val),
      "c"(a->bytes)
     :"memory"); 
}

// A ^= 0x12345678
void api_xor_i(api_t a, const uint32_t val)
{
    uint8_t x[4];
    x[3] = (uint8_t)(val);
    x[2] = (uint8_t)(val >> 8);
    x[1] = (uint8_t)(val >> 16);
    x[0] = (uint8_t)(val >> 24);
    
    asm("   xorl %1, (%0);          "      
    ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
      "a"(*(uint32_t*)(x))
     :"memory"); 
}

// A ^= B
void api_xor_self(api_t a, api_t b)
{
    asm("   .api_xorb_self2:            \
                movb (%1), %2;          \
                xorb %2, (%0);          \
                dec %0;                 \
                dec %1;                 \
                loopnz .api_xorb_self2; "      
    ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
      "r"(b->i + b->bytes - sizeof(uint8_t)), 
      "a"(0),
      "c"(a->bytes >= b->bytes ? b->bytes : a->bytes)
     :"memory"); 
}

// A ^ B = C
void api_xor(api_t a, api_t b, api_t c)
{
    if(cpuid.features.SSE4_1 && !((a->bytes >= b->bytes ? b->bytes : a->bytes) % SSE_ALIGN_SIZE))
    {
        asm("   .api_pxor_sse4:             \
                    movntdqa (%0), %%xmm0;  \
                    pxor (%1), %%xmm0;      \
                    movntdq %%xmm0, (%2);   \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    sub $16, %2;            \
                    loopnz .api_pxor_sse4;  "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(b->i + b->bytes - SSE_ALIGN_SIZE), 
          "r"(c->i + c->bytes - SSE_ALIGN_SIZE),
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / SSE_ALIGN_SIZE)
         :"memory");                  
    }
    if(cpuid.features.SSE2 && !((a->bytes >= b->bytes ? b->bytes : a->bytes) % SSE_ALIGN_SIZE))
    {
        asm("   .api_pxor_sse2:             \
                    movdqa (%0), %%xmm0;    \
                    pxor (%1), %%xmm0;      \
                    movdqa %%xmm0, (%2);    \
                    sub $16, %0;            \
                    sub $16, %1;            \
                    sub $16, %2;            \
                    loopnz .api_pxor_sse2;  "
        ::"r"(a->i + a->bytes - SSE_ALIGN_SIZE), 
          "r"(b->i + b->bytes - SSE_ALIGN_SIZE), 
          "r"(c->i + c->bytes - SSE_ALIGN_SIZE),
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / SSE_ALIGN_SIZE)
         :"memory");                  
    }
    else if(!((a->bytes >= b->bytes ? b->bytes : a->bytes) % sizeof(uint32_t)))
    {
        asm("   .api_xorl2:                 \
                    movl (%1), %3;          \
                    xorl (%0), %3;          \
                    movl %3, (%2);          \
                    sub $4, %0;             \
                    sub $4, %1;             \
                    sub $4, %2;             \
                    loopnz .api_xorl2;      "      
        ::"r"(a->i + a->bytes - sizeof(uint32_t)), 
          "r"(b->i + b->bytes - sizeof(uint32_t)), 
          "r"(c->i + c->bytes - sizeof(uint32_t)),
          "r"(0),
          "c"((a->bytes >= b->bytes ? b->bytes : a->bytes) / sizeof(uint32_t))
         :"memory"); 
    }
    else
    {
        asm("   .api_xorb2:                 \
                    movb (%1), %3;          \
                    xorb (%0), %3;          \
                    movb %3, (%2);          \
                    dec %0;                 \
                    dec %1;                 \
                    dec %2;                 \
                    loopnz .api_xorb2;      "      
        ::"r"(a->i + a->bytes - sizeof(uint8_t)), 
          "r"(b->i + b->bytes - sizeof(uint8_t)), 
          "r"(c->i + c->bytes - sizeof(uint8_t)),
          "r"(0),
          "c"(a->bytes >= b->bytes ? b->bytes : a->bytes)
         :"memory"); 
    }
}

// A * B = C
void api_mul(api_t a, api_t b, api_t c)
{
    api_t tmp = api_init_c(a);
    memclr(tmp->i, tmp->bytes);
    
    asm("   .api_mul_start:             \
                push %0;                \
                push %2;                \
                push %%ecx;             \
                cmpb $0, (%1);          \
                je .api_mul_next;       \
            .api_mul:                   \
                movb (%0), %%al;        \
                cmpb $0, %%al;          \
                je .api_mul_is_zero;    \
                mulb (%1);              \
                addb %%al, (%2);        \
                adcb $0, -1(%2);        \
                addb %%ah, -1(%2);      \
                adcb $0, -2(%2);        \
            .api_mul_is_zero:           \
                dec %0;                 \
                dec %2;                 \
                loopnz .api_mul;        \
            .api_mul_next:              \
                pop %%ecx;              \
                pop %2;                 \
                pop %0;                 \
                dec %2;                 \
                dec %1;                 \
                dec %3;                 \
                cmp $0, %3;             \
                jne .api_mul_start;     "
    ::"r"(a->i + a->bytes - 1),
      "r"(b->i + b->bytes - 1),
      "r"(tmp->i + tmp->bytes - 1),
      "r"(a->bytes),
      "a"(0),
      "c"(a->bytes)
    :"memory");     
    
    memcpy(c->i, tmp->i, tmp->bytes);    
}

// C = A * "1234"
void api_mul_s(api_t a, const char * str, api_t c)
{
    uint8_t * hex = malloc(a->bytes);
    memclr(hex, a->bytes);
    string2hexarray(str, hex, a->bytes);
    
    api_t tmp = api_init_c(a);
    memclr(tmp->i, tmp->bytes);
    
    asm ("   .api_mul_s_start:          \
                push %0;                \
                push %2;                \
                push %%ecx;             \
                cmpb $0, (%1);          \
                je .api_mul_s_next;     \
            .api_mul_s:                 \
                movb (%0), %%al;        \
                cmpb $0, %%al;          \
                je .api_mul_s_is_zero;  \
                mulb (%1);              \
                addb %%al, (%2);        \
                adcb $0, -1(%2);        \
                addb %%ah, -1(%2);      \
                adcb $0, -2(%2);        \
            .api_mul_s_is_zero:         \
                dec %0;                 \
                dec %2;                 \
                loopnz .api_mul_s;      \
            .api_mul_s_next:            \
                pop %%ecx;              \
                pop %2;                 \
                pop %0;                 \
                dec %2;                 \
                dec %1;                 \
                dec %3;                 \
                cmp $0, %3;             \
                jne .api_mul_s_start;   "
    ::"r"(a->i + a->bytes - 1),
      "r"(hex + a->bytes - 1),
      "r"(tmp->i + tmp->bytes - 1),
      "r"(a->bytes),
      "a"(0),
      "c"(a->bytes)
    :"memory");     
    
    memcpy(c->i, tmp->i, tmp->bytes); 
    
    api_destroy(tmp);
}

void api_pow_i(api_t a, const size_t exp)
{
    size_t i;
    
    api_t tmp = api_init_c(a);
    memcpy(tmp->i, a->i, a->bytes);
    
    for(i = 1; i < exp; i++)
    {
        api_mul(a, tmp, a);
    }
    
    api_destroy(tmp);
}






